package com.kaspersky.kaspresso.device.exploit

import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import androidx.test.espresso.Espresso
import androidx.test.uiautomator.UiDevice
import com.kaspersky.kaspresso.device.activities.Activities
import com.kaspersky.kaspresso.device.server.AdbServer
import com.kaspersky.kaspresso.instrumental.InstrumentalDependencyProvider
import com.kaspersky.kaspresso.logger.UiTestLogger

/**
 * The implementation of the [Exploit] interface.
 */
class ExploitImpl(
    private val logger: UiTestLogger,
    private val activities: Activities,
    private val instrumentalDependencyProvider: InstrumentalDependencyProvider,
    private val adbServer: AdbServer
) : Exploit {

    private val uiDevice: UiDevice
        get() = instrumentalDependencyProvider.uiDevice

    /**
     * Toggles the orientation of the device.
     */
    override fun rotate() {
        val resumedActivity = activities.getResumed() ?: return

        val neededDeviceOrientation: Exploit.DeviceOrientation =
            when (resumedActivity.resources.configuration.orientation) {
                ORIENTATION_LANDSCAPE -> Exploit.DeviceOrientation.Portrait
                ORIENTATION_PORTRAIT -> Exploit.DeviceOrientation.Landscape
                else -> throw RuntimeException("Current device orientation is incorrect.")
            }

        setOrientation(neededDeviceOrientation)
        logger.i("Device orientation set to $neededDeviceOrientation")
    }

    /**
     * Sets the specific [Exploit.DeviceOrientation] of the device via shell. Note: sets auto-rotation to false!
     *
     * Required Permissions: INTERNET.
     *
     * @param deviceOrientation the desired orientation of the device.
     */
    override fun setOrientation(deviceOrientation: Exploit.DeviceOrientation) {
        setAutoRotationEnabled(false)

        val value = when (deviceOrientation) {
            Exploit.DeviceOrientation.Portrait -> 0
            Exploit.DeviceOrientation.Landscape -> 1
        }

        adbServer.performShell(
            "content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:$value"
        )
        logger.i("Device orientation set to $deviceOrientation")
    }

    /**
     * Sets the device's auto-rotation, whether it enabled or not, via shell.
     *
     * Required Permissions: INTERNET.
     *
     * @param enabled the desired auto-rotation state.
     */
    override fun setAutoRotationEnabled(enabled: Boolean) {
        val value = if (enabled) 1 else 0

        adbServer.performShell(
            "content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:$value"
        )
        logger.i("Device auto rotation ${if (enabled) "en" else "dis"}abled")
    }

    /**
     * Presses back button on the device.
     *
     * @param failTestIfAppUnderTestClosed if set to true, an exception will be thrown when Espresso navigates
     * outside the application or process under test.
     */
    override fun pressBack(failTestIfAppUnderTestClosed: Boolean) {
        logger.i("Press back button")
        if (failTestIfAppUnderTestClosed) {
            Espresso.pressBack()
        } else {
            Espresso.pressBackUnconditionally()
        }
    }

    /**
     * Presses home button on the device.
     *
     * @return true if successful, else return false.
     */
    override fun pressHome(): Boolean {
        logger.i("Press home button")
        return uiDevice.pressHome()
    }
}
